//! Shader 是一种图形编程脚本,此模块包含与shader相关的任何信息
//!
//!

pub mod assets;
pub mod utils;
pub mod values;
use super::arc::{ArcBindGroupLayout, ArcPipelineLayout, ArcRenderPipeline, ArcShaderModule};
use super::texture::GpuTexture;

use std::collections::{hash_map::DefaultHasher, HashMap};

use fxhash::FxHashMap;

use mango_core::math::{Transform3D, Vec2, Vec3, Vec4};
use mango_core::log::Log;
use mango_core::uuid;

use crate::cache::texture::{SamplerCache, TextureCache};
use crate::gpu::arc::{ArcBindGroup, ArcTextureView};
use crate::gpu::bind_group::{BindGroupBuilder, BindGroupCache, BindGroupLayoutBuilder};

use crate::geometry::surface::SurfaceSharedData;
use crate::material::shader::draw_paramters::DrawParameters;
use crate::resource::texture::{Sampler, TextureResource};
use std::fmt::Debug;

use crate::core::color::Color;
use crate::help::{array_as_u8_slice, value_as_u8_slice};

use crate::material::PropertyValue;

use crate::WgpuState;

use assets::ShaderLib;
use utils::pre_processor::PreProcessor;
use values::reflection::{ShaderReflection, ShaderReflectionVarInfo};

use crate::gpu::arc::ArcBuffer;

use uuid::Uuid;
use wgpu::util::DeviceExt;
pub use wgpu::{BlendComponent, BlendFactor, BlendOperation};
/// Hashable representation of a render pipeline, used as a key in the HashMap cache.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct RenderPipelineInfo {
    pub vs: ArcShaderModule,
    pub fs: ArcShaderModule,
    pub vs_entry: String,
    pub fs_entry: String,
    pub samples: u32,
    pub format: wgpu::TextureFormat,
    pub blend: Option<wgpu::BlendState>,
    pub depth: bool,
    pub vertices: bool,
    pub topology: wgpu::PrimitiveTopology,
    pub cull_mode: Option<wgpu::Face>,
    pub front_face: wgpu::FrontFace,
    pub vertex_layout: wgpu::VertexBufferLayout<'static>,
    pub depth_compare: wgpu::CompareFunction,
    pub guid:Uuid
}

/// Caches both the pipeline *and* the pipeline layout.
#[derive(Debug, Default)]
pub struct PipelineCache {
    pipelines: HashMap<RenderPipelineInfo, ArcRenderPipeline>,
    layouts: HashMap<u64, ArcPipelineLayout>,
}

impl PipelineCache {
    pub fn new() -> Self {
        PipelineCache {
            pipelines: HashMap::new(),
            layouts: HashMap::new(),
        }
    }

    pub fn render_pipeline(
        &mut self,
        device: &wgpu::Device,
        layout: &wgpu::PipelineLayout,
        info: RenderPipelineInfo,
    ) -> ArcRenderPipeline {
        let vertex_buffers = [info.vertex_layout.clone()];
        self.pipelines
            .entry(info.clone())
            .or_insert_with(|| {
                let pipe = ArcRenderPipeline::new(device.create_render_pipeline(
                    &wgpu::RenderPipelineDescriptor {
                        label: None,
                        layout: Some(layout),
                        vertex: wgpu::VertexState {
                            module: &info.vs,
                            entry_point: &info.vs_entry,
                            buffers: if info.vertices { &vertex_buffers } else { &[] },
                        },
                        primitive: wgpu::PrimitiveState {
                            topology: info.topology,
                            strip_index_format: None,
                            front_face: info.front_face,
                            cull_mode: info.cull_mode,
                            unclipped_depth: false,
                            polygon_mode: wgpu::PolygonMode::Fill,
                            conservative: false,
                        },

                        depth_stencil: if info.depth {
                            Some(wgpu::DepthStencilState {
                                format: wgpu::TextureFormat::Depth32Float,
                                depth_write_enabled: true,
                                depth_compare: info.depth_compare,
                                stencil: Default::default(),
                                bias: Default::default(),
                            })
                        } else {
                            None
                        },
                        multisample: wgpu::MultisampleState {
                            count: info.samples,
                            mask: !0,
                            alpha_to_coverage_enabled: false,
                        },

                        fragment: Some(wgpu::FragmentState {
                            module: &*info.fs,
                            entry_point: &info.fs_entry,
                            targets: &[Some(wgpu::ColorTargetState {
                                format: info.format,
                                blend: info.blend,
                                write_mask: wgpu::ColorWrites::ALL,
                            })],
                        }),
                        multiview: None,
                    },
                ));
                pipe
                
            })
            .clone()
    }

    pub fn layout(
        &mut self,
        device: &wgpu::Device,
        bind_groups: &[ArcBindGroupLayout],
    ) -> ArcPipelineLayout {
        let key = {
            use std::hash::{Hash, Hasher};
            let mut h = DefaultHasher::new();
            for bg in bind_groups {
                bg.id().hash(&mut h);
            }
            h.finish()
        };
        self.layouts
            .entry(key)
            .or_insert_with(|| {
                ArcPipelineLayout::new(
                    device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
                        label: None,
                        bind_group_layouts: &bind_groups
                            .iter()
                            .map(|bg| bg.handle.as_ref())
                            .collect::<Vec<_>>(),
                        push_constant_ranges: &[],
                    }),
                )
            })
            .clone()
    }
}

/// Describes the blend mode used when drawing images.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BlendMode {
    /// The blend mode for the color channels.
    pub color: BlendComponent,
    /// The blend mode for the alpha channel.
    pub alpha: BlendComponent,
}

impl BlendMode {
    /// When combining two fragments, add their values together, saturating
    /// at 1.0
    pub const ADD: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::SrcAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::OneMinusDstAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
    };

    /// When combining two fragments, subtract the source value from the
    /// destination value
    pub const SUBTRACT: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::SrcAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::ReverseSubtract,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::Zero,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
    };

    /// When combining two fragments, add the value of the source times its
    /// alpha channel with the value of the destination multiplied by the inverse
    /// of the source alpha channel. Has the usual transparency effect: mixes the
    /// two colors using a fraction of each one specified by the alpha of the source.
    pub const ALPHA: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::SrcAlpha,
            dst_factor: BlendFactor::OneMinusSrcAlpha,
            operation: BlendOperation::Add,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::OneMinusDstAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
    };

    /// When combining two fragments, subtract the destination color from a constant
    /// color using the source color as weight. Has an invert effect with the constant
    /// color as base and source color controlling displacement from the base color.
    /// A white source color and a white value results in plain invert. The output
    /// alpha is same as destination alpha.
    pub const INVERT: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::Constant,
            dst_factor: BlendFactor::Src,
            operation: BlendOperation::Subtract,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::Zero,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
    };

    /// When combining two fragments, multiply their values together (including alpha)
    pub const MULTIPLY: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::Dst,
            dst_factor: BlendFactor::Zero,
            operation: BlendOperation::Add,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::DstAlpha,
            dst_factor: BlendFactor::Zero,
            operation: BlendOperation::Add,
        },
    };

    /// When combining two fragments, choose the source value (including source alpha)
    pub const REPLACE: Self = BlendMode {
        color: wgpu::BlendState::REPLACE.color,
        alpha: wgpu::BlendState::REPLACE.alpha,
    };

    /// When combining two fragments, choose the lighter value
    pub const LIGHTEN: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::SrcAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Max,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::OneMinusDstAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
    };

    /// When combining two fragments, choose the darker value
    pub const DARKEN: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::SrcAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Min,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::OneMinusDstAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
    };

    /// When using premultiplied alpha, use this.
    ///
    /// You usually want to use this blend mode for drawing canvases
    /// containing semi-transparent imagery.
    /// For an explanation on this see: <https://github.com/ggez/ggez/issues/694#issuecomment-853724926>
    pub const PREMULTIPLIED: Self = BlendMode {
        color: BlendComponent {
            src_factor: BlendFactor::One,
            dst_factor: BlendFactor::OneMinusSrcAlpha,
            operation: BlendOperation::Add,
        },
        alpha: BlendComponent {
            src_factor: BlendFactor::OneMinusDstAlpha,
            dst_factor: BlendFactor::One,
            operation: BlendOperation::Add,
        },
    };
}

#[derive(Debug)]
pub struct ShaderBuffer {
    pub value: PropertyValue,
    pub buffer: ArcBuffer,
    pub offset: u64,
    pub visibility: wgpu::ShaderStages,
}

impl ShaderBuffer {
    pub fn new(
        value: PropertyValue,
        buffer: ArcBuffer,
        offset: u64,
        visibility: wgpu::ShaderStages,
    ) -> Self {
        Self {
            value,
            buffer,
            offset,
            visibility,
        }
    }
}

#[derive(Debug, Default)]
pub struct ShaderParams(pub HashMap<String, ShaderBuffer>);

impl std::ops::Deref for ShaderParams {
    type Target = HashMap<String, ShaderBuffer>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl std::ops::DerefMut for ShaderParams {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

#[derive(PartialEq, Eq, Clone)]
pub enum EffectTexture {
    SourceFile(TextureResource),
    View(ArcTextureView),
}

///
/// [ShaderEffect] 是一个用于构建管线的结构体 , 通常包含着色器的布局信息以及其余构建管线所需要的对象信息
/// 一个材质可能会包含多个 [ShaderEffect] ,例如渲染阴影的 shadow pass ShaderEffect 以及渲染网格的 forward pass ShaderEffect
///
///
pub struct Program {
    pub instance_id: uuid::Uuid,
    pub shader_variant: String,
    pub vs_entry_point: String,
    pub fs_entry_point: String,
    pub bind_groups: Vec<ArcBindGroup>,
    pub shader_reflection: Option<ShaderReflection>,
    pub define_value: FxHashMap<String, String>,
    pub const_values: FxHashMap<String, String>,
    pub uniforms: ShaderParams,
    pub textures: FxHashMap<String, EffectTexture>,

    pub shader_change: bool,
    pub state_change: bool,
    pub vs_name: String,
    pub fs_name: String,
    pub vs_source: String,
    dest_vs_source: String,
    pub fs_source: String,
    dest_fs_source: String,
    pub(crate) wgpu_state: WgpuState,
    vs_module: Option<ArcShaderModule>,
    fs_module: Option<ArcShaderModule>,
    pub pipeline: Option<ArcRenderPipeline>,
    pub layout: Option<ArcPipelineLayout>,
    pub bind_group_layout: Vec<ArcBindGroupLayout>,
    groups_shader_reflection_varinfos: Vec<Vec<ShaderReflectionVarInfo>>,
    texture_change: bool,
    texture_group: usize,
}

impl Debug for Program {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("ShaderEffect")
            .field("instance_id", &self.instance_id)
            .field("shader_variant", &self.shader_variant)
            .field("vs_entry_point", &self.vs_entry_point)
            .field("fs_entry_point", &self.fs_entry_point)
            .field("bind_groups", &self.bind_groups)
            .field("shader_reflection", &self.shader_reflection)
            .field("define_value", &self.define_value)
            .field("const_values", &self.const_values)
            .field("uniforms", &self.uniforms)
            // .field("textures", &self.textures)
            .field("shader_change", &self.shader_change)
            .field("state_change", &self.state_change)
            .field("vs_name", &self.vs_name)
            .field("fs_name", &self.fs_name)
            .finish()
    }
}

impl Program {
    pub fn new(
        state: &WgpuState, 
        name: &str, 
        vs_entry: &str,
        fs_entry: &str,
        vertex_source: &str, 
        fragment_source: &str,
        id:Uuid) -> Self {
        let vertex_name = format!("{}_VertexShader", name);
        let fragment_name = format!("{}_FragmentShader", name);

        Program {
            instance_id: id,
            shader_variant: "".to_owned(),
            vs_entry_point: vs_entry.to_owned(),
            fs_entry_point: fs_entry.to_owned(),
            bind_groups: vec![],
            shader_reflection: None,
            define_value: Default::default(),
            const_values: Default::default(),
            uniforms: Default::default(),
            textures: Default::default(),
            shader_change: true,
            state_change: true,
            vs_name: vertex_name,
            fs_name: fragment_name,
            vs_source: vertex_source.to_owned(),
            fs_source: fragment_source.to_owned(),
            wgpu_state: state.clone(),
            vs_module: None,
            fs_module: None,
            pipeline: None,
            layout: None,
            dest_vs_source: "".to_owned(),
            dest_fs_source: "".to_owned(),
            bind_group_layout: vec![],
            groups_shader_reflection_varinfos: vec![],
            texture_change: true,
            texture_group: usize::MAX,
        }
    }

    pub fn pre_compile(&mut self, shader_lib: &mut ShaderLib, geometry: &SurfaceSharedData) {
        self.pre_define(geometry);
        self.pre_compile_shader(wgpu::ShaderStages::VERTEX, self.vs_source.clone());
        self.pre_compile_shader(wgpu::ShaderStages::FRAGMENT, self.fs_source.clone());
        self.gen_reflection(shader_lib);
        self.shader_change = false;
    }

    pub fn gen_reflection(&mut self, shader_lib: &mut ShaderLib) {
        self.shader_variant = ShaderReflection::gen_render_shader_variant(self);

        let vs_pre_shader =
            PreProcessor::parse(shader_lib, &self.dest_vs_source, &mut self.define_value);
        let vs_pre_shader = PreProcessor::parse(shader_lib, &vs_pre_shader, &mut self.define_value);

        ShaderReflection::get_shader_reflection2(shader_lib, &vs_pre_shader, self);

        let fs_pre_shader =
            PreProcessor::parse(shader_lib, &self.dest_fs_source, &mut self.define_value);
        let fs_pre_shader = PreProcessor::parse(shader_lib, &fs_pre_shader, &mut self.define_value);

        ShaderReflection::get_shader_reflection2(shader_lib, &fs_pre_shader, self);
        ShaderReflection::final_apply(self);
    }

    pub fn pre_compile_shader(&mut self, shader_stage: wgpu::ShaderStages, mut shader: String) {
        for (key, value) in self.const_values.iter() {
            shader = shader.replace(key, value)
        }

        if shader_stage == wgpu::ShaderStages::VERTEX {
            self.dest_vs_source = shader;
        } else if shader_stage == wgpu::ShaderStages::FRAGMENT {
            self.dest_fs_source = shader;
        }
    }

    pub fn pre_define(&mut self, _geometry: &SurfaceSharedData) {
        // if self.shader_state.accept_gi {
        //     self.define_value
        //         .insert("USE_GI".to_owned(), "true".to_owned());
        // }

        // if self.shader_state.use_light {
        //     self.define_value
        //         .insert("USE_LIGHT".to_owned(), "true".to_owned());
        // }

        // if geometry.colors.is_some() {
        //     self.define_value
        //         .insert("USE_VERTEXCOLOR".to_owned(), "true".to_owned());
        // }
    }

    fn post_define(&mut self, shader_lib: &mut ShaderLib, code: String) -> String {
        let shader = code;



        // if draw_parameters.rt.color_attachments.len() > 1 {
        //     self.define_value
        //         .insert("USE_WORLDPOS".to_owned(), "true".to_owned());
        //     self.define_value
        //         .insert("USEGBUFFER".to_owned(), "true".to_owned());
        // }

        // if self.shader_state.use_light {
        //     self.define_value
        //         .insert("USE_LIGHT".to_owned(), "true".to_owned());
        // }

        PreProcessor::parse(shader_lib, &shader, &mut self.define_value)
    }
    const MAX_MARCO_COUNT:usize = 64;
    fn compile_shader(
        &mut self,
        stage: wgpu::ShaderStages,
        shader_lib: &mut ShaderLib,
        code: String,
    ) {
        let mut shader = code;
        shader = self.post_define(shader_lib, shader);

        let mut key = shader.clone();

       assert!( self.define_value.len()  < Self::MAX_MARCO_COUNT, "Shader Marco Count Must Less Than 64");
        // let mut key = 

     
        for (k, v) in &self.define_value {
            key.push_str(&format!("{}={},", k, v));
        }
        let shader_module = if let Some(module) = shader_lib.get_shader_module(&key) {
            Log::info("缓存中已存在此shader，读取中......");
            module
        } else {
            Log::info("开始编译shader文件......");
            let _label = if stage == wgpu::ShaderStages::VERTEX {
                &self.vs_name
            } else {
                &self.fs_name
            };
            // let _ = std::fs::write(format!("{}.wgsl", label), &shader);
            let module = ArcShaderModule::new(self.wgpu_state.device.create_shader_module(
                wgpu::ShaderModuleDescriptor {
                    label: if stage == wgpu::ShaderStages::VERTEX {
                        Some(&self.vs_name)
                    } else {
                        Some(&self.fs_name)
                    },
                    source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(&shader)),
                },
            ));
            Log::info("shader编译成功，写入缓存");
            shader_lib.set_shader_module(&key, module.clone());
            module
        };

        if stage == wgpu::ShaderStages::VERTEX {
            self.vs_module = Some(shader_module);
            self.dest_vs_source = shader;
        } else {
            self.fs_module = Some(shader_module);
            self.dest_fs_source = shader;
        }
    }

    fn gen_global_data_group(
        device: &wgpu::Device,
        group_cache: &mut BindGroupCache,
    ) -> ArcBindGroupLayout {
        BindGroupLayoutBuilder::new()
            .buffer(
                wgpu::ShaderStages::FRAGMENT
                    | wgpu::ShaderStages::VERTEX
                    ,
                wgpu::BufferBindingType::Uniform,
                false,
            )
            .buffer(
                wgpu::ShaderStages::FRAGMENT
                    | wgpu::ShaderStages::VERTEX
                    ,
                wgpu::BufferBindingType::Storage { read_only: true },
                false,
            )
            .create(device, group_cache)
    }

    fn gen_groups(
        &mut self,
        state: &WgpuState,
        index: usize,
        resource_manager: &mut TextureCache,
        sampler_cache: &mut SamplerCache,
        group_cache: &mut BindGroupCache,
        force: bool,
    ) {
        if self.bind_groups.get(index).is_none() || force {
            // update bindgroup
            let sampler = sampler_cache.get(&self.wgpu_state.device, Sampler::default());

            let shader_refs = &self.groups_shader_reflection_varinfos[index];
            let mut builder = BindGroupBuilder::new();
            let mut layout_builder = BindGroupLayoutBuilder::new();
            let mut views = vec![];
            let mut use_view = 0;
            for binding in shader_refs {
                if binding.var_ty == "var" && binding.data_ty != "sampler" {
                    if let Some(image) = self.textures.get(&binding.var_name) {
                        match image {
                            EffectTexture::SourceFile(image) => {
                                if let Some(texture_view) =
                                    resource_manager.get_view(state, sampler_cache, image)
                                {
                                    views.push(texture_view)
                                } else {
                                    let white_texture_view = resource_manager.white_dunmy.clone();
                                    
                                    views.push(white_texture_view)
                                }
                            }
                            EffectTexture::View(view) => views.push(view.clone()),
                        }
                    }
                }
            }

            for binding in shader_refs {
                if binding.var_ty == "uniform" {
                    // println!("uniform = {} buffer = {:?}",index,binding.var_name);
                    if let Some(buffer) = self.uniforms.0.get(&binding.var_name) {
                        builder = builder.buffer(
                            &buffer.buffer,
                            buffer.offset,
                            buffer.visibility,
                            wgpu::BufferBindingType::Uniform,
                            false,
                            None,
                        );
                        layout_builder = layout_builder.buffer(
                            buffer.visibility,
                            wgpu::BufferBindingType::Uniform,
                            false,
                        )
                    } else {
                        Log::warn(&format!("uniform {} value is not set! use default uniform, if buffer descriptor is not suit current pipeline, use custom set",binding.var_name));

                        layout_builder = layout_builder.buffer(
                            wgpu::ShaderStages::VERTEX
                                | wgpu::ShaderStages::FRAGMENT
                                ,
                            wgpu::BufferBindingType::Uniform,
                            false,
                        )
                    }
                } else if binding.var_ty == "storage-read" {
                     if let Some(buffer) = self.uniforms.0.get(&binding.var_name) {
                        builder = builder.buffer(
                            &buffer.buffer,
                            buffer.offset,
                            buffer.visibility,
                            wgpu::BufferBindingType::Storage { read_only: true },
                            false,
                            None,
                        );

                        layout_builder = layout_builder.buffer(
                            buffer.visibility,
                            wgpu::BufferBindingType::Storage { read_only: true },
                            false,
                        )
                    } else {
                        Log::warn(&format!("uniform storage-read {} value is not set! use default uniform, if buffer descriptor is not suit current pipeline, use custom set",binding.var_name));
                   
                        layout_builder = layout_builder.buffer(
                            wgpu::ShaderStages::VERTEX
                                | wgpu::ShaderStages::FRAGMENT
                               
                                ,
                            wgpu::BufferBindingType::Storage { read_only: true },
                            false,
                        );
                    }
                } else if binding.var_ty == "var" {
                    if binding.data_ty == "sampler" {
                        builder = builder.sampler(&sampler, wgpu::ShaderStages::FRAGMENT);
                        layout_builder = layout_builder.sampler(wgpu::ShaderStages::FRAGMENT);
                        self.texture_group = index;
                    } else {
                        // TODO texture_2d ,texture_2d_array
                        if binding.data_ty == "texture_2d_array<f32>"
                            
                        {
                            if let Some(views) = views.get(use_view) {
                                builder = builder.image(
                                    views,
                                    wgpu::ShaderStages::FRAGMENT,
                                    wgpu::TextureViewDimension::D2Array,
                                );
                            }
                            layout_builder = layout_builder.image(
                                wgpu::ShaderStages::FRAGMENT,
                                wgpu::TextureViewDimension::D2Array,
                            );
                        } else if binding.data_ty == "texture_cube<f32>" {
                            if let Some(views) = views.get(use_view) {
                                builder = builder.image(
                                    views,
                                    wgpu::ShaderStages::FRAGMENT,
                                    wgpu::TextureViewDimension::Cube,
                                );
                            }
                          
                            layout_builder = layout_builder.image(
                                wgpu::ShaderStages::FRAGMENT,
                                wgpu::TextureViewDimension::Cube,
                            );
                            
                        }else {
                            if let Some(views) = views.get(use_view) {
                                builder = builder.image(
                                    views,
                                    wgpu::ShaderStages::FRAGMENT,
                                    wgpu::TextureViewDimension::D2,
                                );
                            }
                            layout_builder = layout_builder.image(
                                wgpu::ShaderStages::FRAGMENT,
                                wgpu::TextureViewDimension::D2,
                            );
                        }
                        use_view += 1;
                        self.texture_group = index;
                    }
                }
            }
            // println!("group_index = {} ,builder = {:#?}",index,builder);
            let (group, _layout) = builder.create(&self.wgpu_state.device, group_cache);

            let layout = layout_builder.create(&self.wgpu_state.device, group_cache);
            
            self.bind_groups.push(group);
            self.bind_group_layout.push(layout);
            // if self.bind_groups.get(index - 1).is_some() {
            //     self.bind_groups[index - 1] = group;
            //     self.bind_group_layout[index - 1] = layout;
            // } else {
            //     self.bind_groups.push(group);
            //     self.bind_group_layout.push(layout);
            // }
        }
    }

    fn gen_pipeline_layout(
        &mut self,
        state: &WgpuState,
        resource_manager: &mut TextureCache,
        sampler_cache: &mut SamplerCache,
        group_cache: &mut BindGroupCache,
        pipeline_cache: &mut PipelineCache,
    ) {
        self.bind_groups.clear();
        self.bind_group_layout.clear();
        self.groups_shader_reflection_varinfos.clear();
        // self.groups_shader_reflection_varinfos.push(vec![]);
        // self.bind_group_layout.push(Self::gen_global_data_group(
        //     &self.wgpu_state.device,
        //     group_cache,
        // ));

        if let Some(reflection) = &self.shader_reflection {
            for group in reflection.groups.iter() {
                self.groups_shader_reflection_varinfos.push(group.clone());
            }
        } else {
            println!("[error]: can't set empty group");
        }
        // println!("reflection = {:#?}",self.shader_reflection);
        let len = self.groups_shader_reflection_varinfos.len();
        for index in 0..len {
            self.gen_groups(
                state,
                index,
                resource_manager,
                sampler_cache,
                group_cache,
                false,
            )
        }

        // self.bind_groups.iter().for_each(|g|{
        //     println!("group_id = {}",g.id());
        // });
        // println!("group_count = {:?}",self.bind_groups.len());
        // println!("group_layout_count = {:?}",self.bind_group_layout.len());

        self.layout = Some(pipeline_cache.layout(&self.wgpu_state.device, &self.bind_group_layout));
    }

    fn gen_render_pipeline(
        &mut self,
        state: &WgpuState,
        surface: &SurfaceSharedData,
        draw_parameters: &DrawParameters,
        resource_manager: &mut TextureCache,
        sampler_cache: &mut SamplerCache,
        group_cache: &mut BindGroupCache,
        pipeline_cache: &mut PipelineCache,
    ) {
        self.gen_pipeline_layout(
            state,
            resource_manager,
            sampler_cache,
            group_cache,
            pipeline_cache,
        );

        self.create_pipeline(surface, draw_parameters, pipeline_cache);
    }

    fn create_pipeline(
        &mut self,
        surface: &SurfaceSharedData,
        draw_parameters: &DrawParameters,
        pipeline_cache: &mut PipelineCache,
    ) {
        // let targets = &draw_parameters.rt.color_attachments[..];
        // let attributes = &surface.layout.attributes[..] as *const [wgpu::VertexAttribute];
        // let layout = wgpu::VertexBufferLayout {
        //     array_stride: surface.layout.stride,
        //     step_mode: surface.layout.step_mode,
        //     attributes: unsafe { &*attributes },
        // };

        let layout = surface.lock().vertex_buffer.wgpu_layout();
        
        {
            let mut render_pipeline_info = RenderPipelineInfo {
                vs: self.vs_module.as_ref().unwrap().clone(),
                fs: self.fs_module.as_ref().unwrap().clone(),
                vs_entry: "".to_owned(),
                fs_entry: "".to_owned(),
                samples: 1,
                format: self.wgpu_state.surface.read().1.format,
                blend: if let Some(blend) = draw_parameters.blend.clone() {
                    let blend:wgpu::BlendState = blend.into();
                    Some(blend)
                }else {
                    None
                },
                depth: draw_parameters.depth,
                vertices: false,
                topology: draw_parameters.topology.into(),
                cull_mode: draw_parameters.cull_mode.map(|mode|mode.into()),
                front_face: draw_parameters.front_face.into(),
                vertex_layout: layout,
                depth_compare: draw_parameters.depth_compare.into(),
                guid:self.instance_id
            };

            // if draw_parameters.out_color != -1 {
            //     let target = &targets[draw_parameters.out_color as usize];

            //     // if shader_state.blend.is_none() {
            //     //     target
            //     // }
            // }

            if !self.vs_entry_point.is_empty() {
                render_pipeline_info.vs_entry = self.vs_entry_point.clone();
                render_pipeline_info.vertices = true;
                // render_pipeline_info.vertex_layout = layout;
            }

            if !self.fs_entry_point.is_empty() {
                render_pipeline_info.fs_entry = self.fs_entry_point.clone();

                // TODO!
            }

            // if shader_state.multisample > 0 {
            //     // TODO!
            //     todo!("multisample")
            // }

            if let Some(layout) = &self.layout {
                self.pipeline = Some(pipeline_cache.render_pipeline(
                    &self.wgpu_state.device,
                    layout,
                    render_pipeline_info,
                ));

            }
        }
    }

    fn rebuild(
        &mut self,
        state: &WgpuState,
        shader_lib: &mut ShaderLib,
        surface: &SurfaceSharedData,
        draw_parameters: &DrawParameters,
        resource_manager: &mut TextureCache,
        sampler_cache: &mut SamplerCache,
        group_cache: &mut BindGroupCache,
        pipeline_cache: &mut PipelineCache,
        // compile_args:&Vec<String,String>
    ) {
        self.compile_shader(
            wgpu::ShaderStages::VERTEX,
            shader_lib,
            self.dest_vs_source.clone(),
        );
        self.compile_shader(
            wgpu::ShaderStages::FRAGMENT,
            shader_lib,
            self.dest_fs_source.clone(),
        );

        self.gen_render_pipeline(
            state,
            surface,
            draw_parameters,
            resource_manager,
            sampler_cache,
            group_cache,
            pipeline_cache,
        )
    }

    fn update_texture(&mut self, state: &WgpuState, ctx: &mut ShaderTickContext) {
        if let Some(infos) = self
            .groups_shader_reflection_varinfos
            .get(self.texture_group)
        {
            for info in infos {
                if info.var_ty == "texture_2d" {
                    if let Some(EffectTexture::SourceFile(image)) =
                        self.textures.get(&info.var_name)
                    {
                        if ctx
                            .resource_manager
                            .get_texture(state, ctx.sampler_cache, image)
                            .is_none()
                        {
                            return;
                        }
                    }
                }
            }
        }

        self.texture_change = false;

        // println!("texture_change = {:?}",self.texture_group);
        self.gen_groups(
            state,
            self.texture_group,
            ctx.resource_manager,
            ctx.sampler_cache,
            ctx.group_cache,
            true,
        );
    }


    pub fn generate_code(&mut self,
        shader_lib: &mut ShaderLib,
        surface: &SurfaceSharedData,
    ){
        self.pre_compile(shader_lib, surface);
        
    }

    pub fn apply(
        &mut self,
        state: &WgpuState,
        surface: &SurfaceSharedData,
        ctx: &mut ShaderTickContext,
        defines:&FxHashMap<String,String>
    ) {
        // if self.texture_change
        if self.texture_change && self.texture_group != usize::MAX {
            self.update_texture(state, ctx);
        }

        if self.state_change {
            // if self.shader_change {
            //     self.pre_compile(ctx.shader_lib, surface);
            // }
            
            self.define_value = defines.clone();


            self.rebuild(
                state,
                ctx.shader_lib,
                surface,
                ctx.draw_parameters,
                ctx.resource_manager,
                ctx.sampler_cache,
                ctx.group_cache,
                ctx.pipeline_cache,
            );

            self.state_change = false;
        }

        // if let Some(shader_reflection) = self.
    }

    pub fn set_entry_name(&mut self, vs_entry: &str, fs_entry: &str) {
        self.vs_entry_point = vs_entry.to_owned();
        self.fs_entry_point = fs_entry.to_owned();
    }

    // pub fn update(
    //     &mut self,
    //     state: &WgpuState,
    //     geometry: &mut SurfaceSharedData,
    //     ctx: &mut ShaderTickContext,
    // ) {
    //     // self.pre_compile(shader_lib, geometry);
    //     self.apply(state, geometry, ctx)
    // }

    pub fn set_texture(&mut self, name: &str, texture: EffectTexture) {
        match &texture {
            EffectTexture::SourceFile(_image) => {
                // println!("use white texture  ={:?}",target)
            },
            EffectTexture::View(t) => {
                println!("use white texture=======================  ={:?}",t.id());
            },
        }
        if let Some(target) = self.textures.get(name) {
            ;
            if *target != texture {
                self.textures.insert(name.to_owned(), texture);
            }
            self.texture_change = true;
        } else {
            self.textures.insert(name.to_owned(), texture);
            self.texture_change = true;
        }
    }

    pub fn set_base_map(&mut self, value: TextureResource) {
        self.set_texture("baseMap", EffectTexture::SourceFile(value));
    }

    pub fn set_base_map_view(&mut self, value: ArcTextureView) {
        self.set_texture("baseMap", EffectTexture::View(value));
    }

    pub fn set_uniform_float(&mut self, name: &str, value: f32) {
        match self.uniforms.entry(name.to_owned()) {
            std::collections::hash_map::Entry::Occupied(mut occupied) => {
                if let ShaderBuffer {
                    value: PropertyValue::Float(mat),
                    buffer,
                    offset,
                    visibility: _,
                } = occupied.get_mut()
                {
                    if *mat != value {
                        *mat = value;
                        self.wgpu_state.queue.write_buffer(
                            buffer,
                            *offset,
                            value_as_u8_slice(&value),
                        );
                    }
                }
            }
            std::collections::hash_map::Entry::Vacant(vacant) => {
                let buffer = ArcBuffer::new(self.wgpu_state.device.create_buffer_init(
                    &wgpu::util::BufferInitDescriptor {
                        label: Some(&name),
                        contents: value_as_u8_slice(&value),
                        usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
                    },
                ));

                vacant.insert(ShaderBuffer {
                    value: PropertyValue::Float(value),
                    buffer,
                    offset: 0,
                    visibility: wgpu::ShaderStages::VERTEX
                        | wgpu::ShaderStages::FRAGMENT
                        ,
                });
            }
        }
    }

    pub fn set_uniform_struct(&mut self, name: &str, value: FxHashMap<String, PropertyValue>) {
        let structs = value;
        {
            // 1. create uniform buffer
            let mut values = vec![];

            if self.uniforms.contains_key(name) {
                // for (_key, value) in structs.iter() {
                //     match value {
                //         PropertyValue::Float(val) => values.push(*val),
                //         PropertyValue::FloatArray(val) => values.extend_from_slice(&val[..]),

                //         PropertyValue::Int(val) => values.push(*val as f32),
                //         PropertyValue::Bool(_val) => {}
                //         PropertyValue::Vec2(val) => values.extend_from_slice(&val.as_slice()),
                //         PropertyValue::Vec3(val) => values.extend_from_slice(&val.as_slice()),
                //         PropertyValue::Vec4(val) => values.extend_from_slice(&val.as_slice()),

                //         PropertyValue::Matrix2(val) => values.extend_from_slice(&val.as_slice()),
                //         PropertyValue::Transform2D(val) => values.extend_from_slice(&val.as_slice()),
                //         PropertyValue::Transform3D(val) => values.extend_from_slice(&val.as_slice()),

                //         PropertyValue::UInt(val) => values.push(*val as f32),
                //         PropertyValue::IntArray(val) => values.extend_from_slice(
                //             val.iter()
                //                 .map(|i| *i as f32)
                //                 .collect::<Vec<f32>>()
                //                 .as_slice(),
                //         ),
                //         PropertyValue::UIntArray(val) => values.extend_from_slice(
                //             val.iter()
                //                 .map(|i| *i as f32)
                //                 .collect::<Vec<f32>>()
                //                 .as_slice(),
                //         ),
                //         PropertyValue::Vec2Array(val) => {
                //             let mut temp_values = Vec::with_capacity(val.len() * 2);
                //             val.iter()
                //                 .for_each(|i| temp_values.extend_from_slice(i.as_slice()));
                //             values.extend_from_slice(temp_values.as_slice());
                //         }
                //         PropertyValue::Vec3Array(_val) => todo!(),
                //         PropertyValue::Vec4Array(_val) => todo!(),
                //         PropertyValue::Matrix2Array(_) => todo!(),
                //         PropertyValue::Transform2DArray(_) => todo!(),
                //         PropertyValue::Transform3DArray(_) => todo!(),
                //         PropertyValue::Sampler {
                //             value: _,
                //             fallback: _,
                //         } => todo!(),
                //         PropertyValue::Color(color) => {
                //             values.extend_from_slice(color.srgb_to_linear_f32().as_slice())
                //         }
                //         // PropertyValue::Texture(val) => {}
                //     }
                // }

                // let buffer = self.uniforms.get(name).unwrap().buffer.clone();

                // self.wgpu_state.queue.write_buffer(&buffer, 0,array_as_u8_slice(&value));
            } else {
                for (_key, value) in structs.iter() {
                    match value {
                        PropertyValue::Float(val) => values.push(*val),
                        PropertyValue::FloatArray(val) => values.extend_from_slice(&val[..]),

                        PropertyValue::Int(val) => values.push(*val as f32),
                        PropertyValue::Bool(_val) => {}
                        PropertyValue::Vec2(val) => values.extend_from_slice(&val.to_col_array()),
                        PropertyValue::Vec3(val) => values.extend_from_slice(&val.to_col_array()),
                        PropertyValue::Vec4(val) => values.extend_from_slice(&val.to_col_array()),

                        // PropertyValue::Matrix2(val) => values.extend_from_slice(&val.as_slice()),
                        PropertyValue::Transform2D(val) => values.extend_from_slice(&val.to_col_array()),
                        PropertyValue::Transform3D(val) => values.extend_from_slice(&val.to_col_array()),

                        PropertyValue::UInt(val) => values.push(*val as f32),
                        PropertyValue::IntArray(val) => values.extend_from_slice(
                            val.iter()
                                .map(|i| *i as f32)
                                .collect::<Vec<f32>>()
                                .as_slice(),
                        ),
                        PropertyValue::UIntArray(val) => values.extend_from_slice(
                            val.iter()
                                .map(|i| *i as f32)
                                .collect::<Vec<f32>>()
                                .as_slice(),
                        ),
                        PropertyValue::Vec2Array(val) => {
                            let mut temp_values = Vec::with_capacity(val.len() * 2);
                            val.iter()
                                .for_each(|i| temp_values.extend_from_slice(&i.to_col_array()));
                            values.extend_from_slice(temp_values.as_slice());
                        }
                        PropertyValue::Vec3Array(_val) => todo!(),
                        PropertyValue::Vec4Array(_val) => todo!(),
                        // PropertyValue::Matrix2Array(_) => todo!(),
                        PropertyValue::Transform2DArray(_) => todo!(),
                        PropertyValue::Transform3DArray(_) => todo!(),
                        PropertyValue::Sampler {
                            value: _,
                            fallback: _,
                        } => todo!(),
                        PropertyValue::Color(color) => {
                            todo!()
                            // values.extend_from_slice(color.srgb_to_linear_f32().as_slice())
                        }
                        PropertyValue::ColorArray(_) => todo!(),
                        // PropertyValue::Texture(val) => {}
                    }
                }

                // println!("values = {:?}",values);
                let buffer = ArcBuffer::new(self.wgpu_state.device.create_buffer_init(
                    &wgpu::util::BufferInitDescriptor {
                        label: Some(name),
                        contents: array_as_u8_slice(&values[..]),
                        usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
                    },
                ));
                self.uniforms.insert(
                    name.to_owned(),
                    ShaderBuffer {
                        value: PropertyValue::Float(0.0),
                        buffer: buffer.clone(),
                        offset: 0,
                        visibility: wgpu::ShaderStages::VERTEX
                            | wgpu::ShaderStages::FRAGMENT
                            ,
                    },
                );

                let mut offset = 0;
                for (key, value) in structs {
                    let size = std::mem::size_of_val(&value) as u64;
                    self.uniforms.insert(
                        format!("{}.{}", name, key),
                        ShaderBuffer {
                            value,
                            buffer: buffer.clone(),
                            offset,
                            visibility: wgpu::ShaderStages::VERTEX
                                | wgpu::ShaderStages::FRAGMENT
                                ,
                        },
                    );

                    offset += size;
                }
            }
        }
    }

    pub fn set_uniform_int(&mut self, name: &str, value: i32) {
        if let Some(ShaderBuffer {
            value: PropertyValue::Int(val),
            buffer,
            offset,
            visibility: _,
        }) = self.uniforms.get_mut(name)
        {
            if *val != value {
                *val = value;
                self.wgpu_state
                    .queue
                    .write_buffer(buffer, *offset, array_as_u8_slice(&[value]));
            }
        }
    }

    pub fn set_uniform_vec2f(&mut self, name: &str, value: Vec2) {
        match self.uniforms.entry(name.to_owned()) {
            std::collections::hash_map::Entry::Occupied(mut occupied) => {
                if let ShaderBuffer {
                    value: PropertyValue::Vec2(val),
                    buffer,
                    offset,
                    visibility: _,
                } = occupied.get_mut()
                {
                    if *val != value {
                        *val = value;
                        self.wgpu_state.queue.write_buffer(
                            buffer,
                            *offset,
                            value_as_u8_slice(&value),
                        );
                    }
                }
            }
            std::collections::hash_map::Entry::Vacant(vacant) => {
                let buffer = ArcBuffer::new(self.wgpu_state.device.create_buffer_init(
                    &wgpu::util::BufferInitDescriptor {
                        label: Some(&name),
                        contents: value_as_u8_slice(&value),
                        usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
                    },
                ));

                vacant.insert(ShaderBuffer {
                    value: PropertyValue::Vec2(value),
                    buffer,
                    offset: 0,
                    visibility: wgpu::ShaderStages::VERTEX
                        | wgpu::ShaderStages::FRAGMENT
                        ,
                });
            }
        }
       
    }

    pub fn set_uniform_vec2f_arr(&mut self, name: &str, value: Vec<Vec2>) {
        if let Some(ShaderBuffer {
            value: PropertyValue::Vec2Array(val),
            buffer,
            offset,
            visibility: _,
        }) = self.uniforms.get_mut(name)
        {
            if *val != value {
                self.wgpu_state
                    .queue
                    .write_buffer(buffer, *offset, array_as_u8_slice(&value[..]));
                *val = value;
            }
        }
    }

    pub fn set_uniform_vec3f(&mut self, name: &str, value: Vec3) {
        match self.uniforms.entry(name.to_owned()) {
            std::collections::hash_map::Entry::Occupied(mut occupied) => {
                if let ShaderBuffer {
                    value: PropertyValue::Vec3(mat),
                    buffer,
                    offset,
                    visibility: _,
                } = occupied.get_mut()
                {
                    if *mat != value {
                        *mat = value;
                        self.wgpu_state.queue.write_buffer(
                            buffer,
                            *offset,
                            array_as_u8_slice(&value.to_col_array()),
                        );
                    }
                }
            }
            std::collections::hash_map::Entry::Vacant(vacant) => {
                let buffer = ArcBuffer::new(self.wgpu_state.device.create_buffer_init(
                    &wgpu::util::BufferInitDescriptor {
                        label: Some(&name),
                        contents: array_as_u8_slice(&value.to_col_array()),
                        usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
                    },
                ));

                vacant.insert(ShaderBuffer {
                    value: PropertyValue::Vec3(value),
                    buffer,
                    offset: 0,
                    visibility: wgpu::ShaderStages::VERTEX
                        | wgpu::ShaderStages::FRAGMENT
                        ,
                });
            }
        }
    }

    pub fn set_uniform_vec4f(&mut self, name: &str, value: Vec4) {
        match self.uniforms.entry(name.to_owned()) {
            std::collections::hash_map::Entry::Occupied(mut occupied) => {
                if let ShaderBuffer {
                    value: PropertyValue::Vec4(mat),
                    buffer,
                    offset,
                    visibility: _,
                } = occupied.get_mut()
                {
                    if *mat != value {
                        *mat = value;
                        self.wgpu_state.queue.write_buffer(
                            buffer,
                            *offset,
                            array_as_u8_slice(&value.to_col_array()),
                        );
                    }
                }
            }
            std::collections::hash_map::Entry::Vacant(vacant) => {
                let buffer = ArcBuffer::new(self.wgpu_state.device.create_buffer_init(
                    &wgpu::util::BufferInitDescriptor {
                        label: Some(&name),
                        contents: array_as_u8_slice(&value.to_col_array()),
                        usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
                    },
                ));

                vacant.insert(ShaderBuffer {
                    value: PropertyValue::Vec4(value),
                    buffer,
                    offset: 0,
                    visibility: wgpu::ShaderStages::VERTEX
                        | wgpu::ShaderStages::FRAGMENT
                        ,
                });
            }
        }
    }
    pub fn set_uniform_Transform3D(&mut self, name: &str, value: Transform3D) {
        match self.uniforms.entry(name.to_owned()) {
            std::collections::hash_map::Entry::Occupied(mut occupied) => {
                if let ShaderBuffer {
                    value: PropertyValue::Transform3D(mat),
                    buffer,
                    offset,
                    visibility: _,
                } = occupied.get_mut()
                {
                    if *mat != value {
                        *mat = value;
                        self.wgpu_state.queue.write_buffer(
                            buffer,
                            *offset,
                            array_as_u8_slice(&value.to_col_array()),
                        );
                    }
                }
            }
            std::collections::hash_map::Entry::Vacant(vacant) => {
                let buffer = ArcBuffer::new(self.wgpu_state.device.create_buffer_init(
                    &wgpu::util::BufferInitDescriptor {
                        label: Some(&name),
                        contents: array_as_u8_slice(&value.to_col_array()),
                        usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
                    },
                ));

                vacant.insert(ShaderBuffer {
                    value: PropertyValue::Transform3D(value),
                    buffer,
                    offset: 0,
                    visibility: wgpu::ShaderStages::VERTEX
                        | wgpu::ShaderStages::FRAGMENT
                        ,
                });
            }
        }
    }

    pub fn set_uniform_Transform3D_array(&mut self, name: &str, value: Vec<Transform3D>) {
        if let Some(ShaderBuffer {
            value: PropertyValue::Transform3DArray(mat),
            buffer,
            offset,
            visibility: _,
        }) = self.uniforms.get_mut(name)
        {
            if mat.len() >= value.len() {
                self.wgpu_state
                    .queue
                    .write_buffer(buffer, *offset, array_as_u8_slice(&value));
                *mat = value;
            }
        } else {
            let buffer = ArcBuffer::new(self.wgpu_state.device.create_buffer_init(
                &wgpu::util::BufferInitDescriptor {
                    label: Some(&name),
                    contents: array_as_u8_slice(&value),
                    usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
                },
            ));
            self.uniforms.insert(
                name.to_owned(),
                ShaderBuffer {
                    value: PropertyValue::Transform3DArray(value),
                    buffer,
                    offset: 0,
                    visibility: wgpu::ShaderStages::VERTEX
                        | wgpu::ShaderStages::FRAGMENT
                        ,
                },
            );
        }
    }

    pub fn set_uniform_color(&mut self, name: &str, value: Color) {
        match self.uniforms.entry(name.to_owned()) {
            std::collections::hash_map::Entry::Occupied(mut occupied) => {
                if let ShaderBuffer {
                    value: PropertyValue::Color(color),
                    buffer,
                    offset,
                    visibility: _,
                } = occupied.get_mut()
                {
                    if *color != value {
                        *color = value;
                        self.wgpu_state.queue.write_buffer(
                            buffer,
                            *offset,
                            array_as_u8_slice(&value.to_normalized_gamma_f32()),
                        );
                    }
                }
            }
            std::collections::hash_map::Entry::Vacant(vacant) => {
                let buffer = ArcBuffer::new(self.wgpu_state.device.create_buffer_init(
                    &wgpu::util::BufferInitDescriptor {
                        label: Some(&name),
                        contents: array_as_u8_slice(&value.to_normalized_gamma_f32()),
                        usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
                    },
                ));

                vacant.insert(ShaderBuffer {
                    value: PropertyValue::Color(value),
                    buffer,
                    offset: 0,
                    visibility: wgpu::ShaderStages::VERTEX
                        | wgpu::ShaderStages::FRAGMENT
                        ,
                });
            }
        }
    }

    pub fn set_uniform_color_array(&mut self, name: &str, value: Vec<Color>) {
        match self.uniforms.entry(name.to_owned()) {
            std::collections::hash_map::Entry::Occupied(mut occupied) => {
                if let ShaderBuffer {
                    value: PropertyValue::ColorArray(color),
                    buffer,
                    offset,
                    visibility: _,
                } = occupied.get_mut()
                {
                    if color.len() != value.len() {
                        let mut data = Vec::with_capacity(value.len() * 4);
                        value.iter().for_each(|v| {
                            data.extend_from_slice(&v.to_normalized_gamma_f32());
                        });
                        self.wgpu_state.queue.write_buffer(
                            buffer,
                            *offset,
                            array_as_u8_slice(&data),
                        );
                        *color = value;
                    }
                }
            }
            std::collections::hash_map::Entry::Vacant(vacant) => {
                let mut data = Vec::with_capacity(value.len() * 4);
                value.iter().for_each(|v| {
                    data.extend_from_slice(&v.to_normalized_gamma_f32());
                });
                let buffer = ArcBuffer::new(self.wgpu_state.device.create_buffer_init(
                    &wgpu::util::BufferInitDescriptor {
                        label: Some(&name),
                        contents: array_as_u8_slice(&data),
                        usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
                    },
                ));

                vacant.insert(ShaderBuffer {
                    value: PropertyValue::ColorArray(value),
                    buffer,
                    offset: 0,
                    visibility: wgpu::ShaderStages::VERTEX
                        | wgpu::ShaderStages::FRAGMENT
                        ,
                });
            }
        }
    }
}

pub struct ShaderTickContext<'tick> {
    pub draw_parameters: &'tick DrawParameters,
    pub shader_lib: &'tick mut ShaderLib,
    pub resource_manager: &'tick mut TextureCache,
    pub sampler_cache: &'tick mut SamplerCache,
    pub group_cache: &'tick mut BindGroupCache,
    pub pipeline_cache: &'tick mut PipelineCache,
}

pub struct PreCompileShaderCtx<'compile> {
    pub shader_lib: &'compile mut ShaderLib,
    pub resource_manager: &'compile mut TextureCache,
    pub sampler_cache: &'compile mut SamplerCache,
    pub group_cache: &'compile mut BindGroupCache,
    pub pipeline_cache: &'compile mut PipelineCache,
}

pub struct CreateEffectInfo<'effect> {
    pub wgpu_state: &'effect WgpuState,
    pub shader_lib: &'effect ShaderLib,
    pub vs_name: &'effect str,
    pub fs_name: &'effect str,
}

pub fn build_effect(info: CreateEffectInfo) -> Program {
    if !info.shader_lib.has_shader(info.vs_name) {
        panic!(
            "shader {} 还未注册,请先使用 `shader_lib.register` 注册后使用",
            info.vs_name
        );
    }
    if !info.shader_lib.has_shader(info.fs_name) {
        panic!(
            "shader {} 还未注册,请先使用 `shader_lib.register` 注册后使用",
            info.fs_name
        );
    }

    let vs_source = info.shader_lib.get_shader(info.vs_name);
    let fs_source = info.shader_lib.get_shader(info.fs_name);

    Program {
        instance_id: Uuid::new_v4(),
        shader_variant: "".to_owned(),
        vs_entry_point: "main".to_owned(),
        fs_entry_point: "main".to_owned(),
        bind_groups: vec![],
        shader_reflection: None,
        define_value: Default::default(),
        const_values: Default::default(),
        uniforms: Default::default(),
        textures: Default::default(),
        shader_change: true,
        state_change: true,
        vs_name: info.vs_name.to_owned(),
        fs_name: info.fs_name.to_owned(),
        vs_source: vs_source.to_owned(),
        fs_source: fs_source.to_owned(),
        wgpu_state: info.wgpu_state.clone(),
        vs_module: None,
        fs_module: None,
        pipeline: None,
        layout: None,
        dest_vs_source: "".to_owned(),
        dest_fs_source: "".to_owned(),
        bind_group_layout: vec![],
        groups_shader_reflection_varinfos: vec![],
        texture_change: true,
        texture_group: usize::MAX,
    }
}

impl Program {
    // pub fn apply(&mut self , mesh:)
}

///
/// [ShaderPass] 包含了由 ShaderEffect 所构造的管线对象
///
/// ```rust
///
/// let texture_lit = build_effect(scene_renderer,'default_mesh_ssbo.vert.wgsl','texture_lit.frag.wgsl');
/// let default_lit = build_effect(scene_renderer,'default_mesh_ssbo.vert.wgsl','default_lit.frag.wgsl');
///
/// let texture_lit_pass = build_pass(scene_renderer,forward_build,texture_lit);
/// let default_lit_pass = build_pass(scene_renderer,forward_build,default_lit);
///
///
///
///
/// ```
///
// #[derive(Clone)]
pub struct ShaderPass {
    pub effect: Program,
    pub enable: bool,
    pub sort: u32,
    pub transparent: bool,
}

impl ShaderPass {}

pub fn build_pass(create_info: CreateEffectInfo) -> ShaderPass {
    let effect = build_effect(create_info);

    ShaderPass {
        effect,
        enable: true,
        sort: 3000,
        transparent: false,
    }
}

pub struct ComputeShader {
    pub entry_point: String,
    pub worker_size_x: u32,
    pub worker_size_y: u32,
    pub worker_size_z: u32,
    compute_pipeline: wgpu::ComputePipeline,
    cs_shader_module: ArcShaderModule,
    dest_cs: String,
    source_cs: String,
    storage_texture: FxHashMap<String, GpuTexture>,
    sample_texture: FxHashMap<String, GpuTexture>,
    groups_shader_reflection_var_infos: ShaderReflectionVarInfo,
    // grou_cache
}
